#ifndef __LIBCO_LOG_H__
#define __LIBCO_LOG_H__

#include <iostream>
#include <fstream>
#include "mutex.hpp"
#include "util.h"

namespace libco {
    
// 定义颜色常量

#define RESET   "\033[0m"
#define RED     "\033[31m"
#define GREEN   "\033[32m"
#define YELLOW  "\033[33m"
#define BLUE    "\033[34m"
#define MAGENTA "\033[35m"
#define CYAN    "\033[36m"
#define GLUE    "\x1b[90m"

// 定义日志级别

#define LOG_LEVEL_DEBUG 0
#define LOG_LEVEL_INFO 1
#define LOG_LEVEL_ERROR 2

static Mutex s_colog_mutex;
static uint8_t s_log_level = LOG_LEVEL_DEBUG;
static std::ofstream s_out_file_stream("log.txt");
static std::string s_log_level_str[3] = {"[DEBUG]", "[INFO]", "[ERROR]"};

class LogStream {
public:

    LogStream(const std::string& path, int line, const std::string& time, const double elapsed, pid_t t_id, const std::string& t_name, uint64_t c_id, const std::string& c_role) {
        std::ostringstream tmp;
        tmp << std::fixed << std::setprecision(3) << elapsed / 1000 << "ms";
        m_elapsed = tmp.str();

        size_t pos = path.find_last_of("/\\");
        m_file_line = path.substr(pos+1) + ": " + std::to_string(line);

        m_level = "[DEBUG]";
        m_time = time;

        tmp = std::ostringstream();
        tmp << "{thread_" << t_id << ": " << t_name << "}";
        m_thread_info = tmp.str();
        tmp = std::ostringstream();
        tmp << "{co_" << c_id << ": " << c_role << "}";
        m_coroutine_info = tmp.str();
    }

    LogStream(uint8_t level, const std::string& path, int line, const std::string& time, const double elapsed, pid_t t_id, const std::string& t_name, uint64_t c_id, const std::string& c_role) {
        if (level >= s_log_level) {
            std::ostringstream tmp;
            tmp << std::fixed << std::setprecision(3) << elapsed / 1000 << "ms";
            m_elapsed = tmp.str();

            size_t pos = path.find_last_of("/\\");
            m_file_line = path.substr(pos+1) + ": " + std::to_string(line);

            m_level = s_log_level_str[level];
            m_time = time;

            tmp = std::ostringstream();
            tmp << "{thread_" << t_id << ": " << t_name << "}";
            m_thread_info = tmp.str();
            
            tmp = std::ostringstream();
            tmp << "{co_" << c_id << ": " << c_role << "}";
            m_coroutine_info = tmp.str();
        }
    }

    ~LogStream() {
        libco::Mutex::Lock lock(s_colog_mutex);
        std::cout << std::left 
                  << RED << std::setw(10) << m_level
                  << CYAN << std::setw(25) << m_time
                  << GLUE << std::setw(15) << m_elapsed
                  << BLUE << std::setw(25) << m_file_line
                  << MAGENTA << std::setw(32) << m_thread_info
                  << YELLOW << std::setw(26) << m_coroutine_info
                  << GREEN << std::setw(40) << m_msg.str() 
                  << RESET << std::endl;
    }

    template <typename T>
    LogStream& operator<<(const T& msg) {
        m_msg << msg;
        return *this;
    }

private:
    std::string m_level;
    std::string m_time;
    std::string m_elapsed;
    std::string m_file_line;
    std::string m_thread_info;
    std::string m_coroutine_info;
    std::string m_co_role;
    std::ostringstream m_msg;
};

/**
 * @brief 线程内部日志器，向thread_local IO流写入
 * 
 */
class LogStreamLocal {
public:

    
    LogStreamLocal(std::stringstream* out, const std::string& path, int line, const std::string& time, const double elapsed, pid_t t_id, const std::string& t_name, uint64_t c_id, const std::string& c_role) {
        std::ostringstream tmp;
        tmp << std::fixed << std::setprecision(3) << elapsed / 1000 << "ms";
        m_elapsed = tmp.str();

        size_t pos = path.find_last_of("/\\");
        m_file_line = path.substr(pos+1) + ":" + std::to_string(line);

        m_level = "[DEBUG]";
        m_time = time;

        tmp = std::ostringstream();
        tmp << "{thread_" << t_id << ": " << t_name << "}";
        m_thread_info = tmp.str();
        
        tmp = std::ostringstream();
        tmp << "{co_" << c_id << ": " << c_role << "}";
        m_coroutine_info = tmp.str();

        m_out = out;
    }

    LogStreamLocal(std::stringstream* out, uint8_t level, const std::string& path, int line, const std::string& time, const double elapsed, pid_t t_id, const std::string& t_name, uint64_t c_id, const std::string& c_role) {
        if (level >= s_log_level) {
            std::ostringstream tmp;
            tmp << std::fixed << std::setprecision(3) << elapsed / 1000 << "ms";
            m_elapsed = tmp.str();

            size_t pos = path.find_last_of("/\\");
            m_file_line = path.substr(pos+1) + ":" + std::to_string(line);

            m_level = s_log_level_str[level];
            m_time = time;

            tmp = std::ostringstream();
            tmp << "{thread_" << t_id << ": " << t_name << "}";
            m_thread_info = tmp.str();
            
            tmp = std::ostringstream();
            tmp << "{co_" << c_id << ": " << c_role << "}";
            m_coroutine_info = tmp.str();

            m_out = out;
        }
    }

    ~LogStreamLocal() {
        libco::Mutex::Lock lock(s_colog_mutex);
        *m_out << std::left 
                  << RED << std::setw(10) << m_level
                  << CYAN << std::setw(30) << m_time
                  << GLUE << std::setw(15) << m_elapsed
                  << BLUE << std::setw(30) << m_file_line
                  << MAGENTA << std::setw(40) << m_thread_info
                  << YELLOW << std::setw(30) << m_coroutine_info
                  << GREEN << std::setw(40) << m_msg.str() 
                  << RESET << std::endl;
    }

    template <typename T>
    LogStreamLocal& operator<<(const T& msg) {
        m_msg << msg;
        return *this;
    }

private:
    std::string m_level;
    std::string m_time;
    std::string m_elapsed;
    std::string m_file_line;
    std::string m_thread_info;
    std::string m_coroutine_info;
    std::string m_co_role;
    std::ostringstream m_msg;
    std::stringstream* m_out;
};

}

/// 宏定义方便使用

#define LIBCO_LOG libco::LogStream(__FILE__, __LINE__, libco::GetCurrentTimeStr(), libco::GetElapsedUs(), libco::GetThreadId(), libco::GetThreadName(), libco::GetCoroutineId(), libco::GetCoroutineRole())
#define LIBCO_LOG_DEBUG libco::LogStream(LOG_LEVEL_DEBUG ,__FILE__, __LINE__, libco::GetCurrentTimeStr(), libco::GetElapsedUs(), libco::GetThreadId(), libco::GetThreadName(), libco::GetCoroutineId(), libco::GetCoroutineRole())
#define LIBCO_LOG_INFO libco::LogStream(LOG_LEVEL_INFO,__FILE__, __LINE__, libco::GetCurrentTimeStr(), libco::GetElapsedUs(), libco::GetThreadId(), libco::GetThreadName(), libco::GetCoroutineId(), libco::GetCoroutineRole())
#define LIBCO_LOG_ERROR libco::LogStream(LOG_LEVEL_ERROR, __FILE__, __LINE__, libco::GetCurrentTimeStr(), libco::GetElapsedUs(), libco::GetThreadId(), libco::GetThreadName(), libco::GetCoroutineId(), libco::GetCoroutineRole())

#define LIBCO_LOG_SS(out) libco::LogStreamLocal(out, __FILE__, __LINE__, libco::GetCurrentTimeStr(), libco::GetElapsedUs(), libco::GetThreadId(), libco::GetThreadName(), libco::GetCoroutineId(), libco::GetCoroutineRole())
#define LIBCO_LOG_SS_DEBUG(out) libco::LogStreamLocal(out, LOG_LEVEL_DEBUG ,__FILE__, __LINE__, libco::GetCurrentTimeStr(), libco::GetElapsedUs(), libco::GetThreadId(), libco::GetThreadName(), libco::GetCoroutineId(), libco::GetCoroutineRole())
#define LIBCO_LOG_SS_INFO(out) libco::LogStreamLocal(out, LOG_LEVEL_INFO,__FILE__, __LINE__, libco::GetCurrentTimeStr(), libco::GetElapsedUs(), libco::GetThreadId(), libco::GetThreadName(), libco::GetCoroutineId(), libco::GetCoroutineRole())
#define LIBCO_LOG_SS_ERROR(out) libco::LogStreamLocal(out, LOG_LEVEL_ERROR, __FILE__, __LINE__, libco::GetCurrentTimeStr(), libco::GetElapsedUs(), libco::GetThreadId(), libco::GetThreadName(), libco::GetCoroutineId(), libco::GetCoroutineRole())

#endif


